package com.ibm.spatiotemp.tools;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.Gson;
import com.ibm.spatiotemp.utility.JsonInterface;

import edu.umn.cs.spatialHadoop.core.Circle;
import edu.umn.cs.spatialHadoop.core.Point;
import edu.umn.cs.spatialHadoop.core.Rectangle;
import edu.umn.cs.spatialHadoop.core.Shape;

public class jsonParser {
	public static Logger logger = Logger.getLogger(jsonParser.class);

	private static ObjectMapper mapper = new ObjectMapper();
	// static private org.json.simple.parser.JSONParser parser = new
	// org.json.simple.parser.JSONParser();
	static private Gson gson = new Gson();

	public static String jsonCollectionFormat = "[%s]";
	public static <T extends JsonInterface> String collecitontoJsonString(Collection<T> coll) {
		StringBuffer strbuf = new StringBuffer();
		Iterator<T> iter = coll.iterator();
		if (iter.hasNext()) {
			JsonInterface obj = iter.next();
			strbuf.append(obj.toJsonString());
		}
		while (iter.hasNext()) {
			JsonInterface obj = iter.next();
			strbuf.append(",");
			strbuf.append(obj.toJsonString());
		}
		return String.format(jsonCollectionFormat, strbuf.toString());
	}
	
	static public JsonNode newJSONNode(Object v) {
		return mapper.valueToTree(v);
	}

	static public ObjectNode newObjectNode() {
		return mapper.createObjectNode();
	}

	static public ArrayNode newArrayNode() {
		return mapper.createArrayNode();
	}
	
	static public String toJsonString(Object obj) {
		return gson.toJson(obj);
	}

	static public Object fromJsonString(String json, Type t) {
		return gson.fromJson(json, t);
	}

	static public ArrayNode objsToArray(ObjectNode[] objs) {
		ArrayNode msgArr = newArrayNode();
		for (ObjectNode rec : objs) {
			msgArr.add(rec);
		}
		return msgArr;
	}


	static public JsonNode fromString(String json) {
		if (json == null) {
			return newObjectNode();
		}
		try {
			return mapper.readTree(json);
		} catch (IOException e) {
		}
		return null;
	}

	/*postJson following GeoJson Format given below:
	 * {"type":"Point","coordinates":[0.0,500.0]}
	 * { "type": "Feature","bbox": [-180.0, -90.0, 180.0, 90.0],"geometry": {...}}
	 * { "type": "Circle","geometry": {"type":"Point","coordinates":[0.0,500.0]},"r":5.0}
	 * */
	static public Shape getShapefromJsonNode(JsonNode postJson)
	{
		String shapeclass = postJson.get("type").asText();
		if(shapeclass.equalsIgnoreCase("point")){
			ArrayNode arraynode = (ArrayNode) postJson.get("coordinates");
			double x = arraynode.get(0).asDouble();
			double y = arraynode.get(1).asDouble();
			return new Point(x,y);
		}
		else if(shapeclass.equalsIgnoreCase("Feature")){
			ArrayNode arraynode = (ArrayNode) postJson.get("bbox");
			double x1 = arraynode.get(0).asDouble();
			double y1 = arraynode.get(1).asDouble();
			double x2 = arraynode.get(2).asDouble();
			double y2 = arraynode.get(3).asDouble();
			
			return new Rectangle(x1,y1,x2,y2);
		}
		else if(shapeclass.equalsIgnoreCase("Circle")){
			JsonNode node = postJson.get("geometry");
			ArrayNode arraynode = (ArrayNode) node.get("coordinates");
			double x = arraynode.get(0).asDouble();
			double y = arraynode.get(1).asDouble();
			double r = postJson.get("r").asDouble();
			return new Circle(x,y,r);
		}
		logger.error("undefined graphical object");
		return null;
	}

	
	static public String GetString(String tag, JsonNode postJson,
			String defaultValue) {
		/* process the int type param */
		JsonNode valueNode = postJson.get(tag);
		if (valueNode == null) {
			return defaultValue;
		}
		return valueNode.asText();
	}

	static public int GetInteger(String tag, JsonNode postJson, int defaultValue) {
		/* process the int type param */
		JsonNode valueNode = postJson.get(tag);
		if (valueNode == null) {
			return defaultValue;
		}
		return valueNode.asInt();
	}
	
	static public long GetLong(String tag, JsonNode postJson, long defaultValue) {
		/* process the int type param */
		JsonNode valueNode = postJson.get(tag);
		if (valueNode == null) {
			return defaultValue;
		}
		return valueNode.asLong();
	}

	static public String[] GetArray(String tag, JsonNode postJson) {
		/* process the int type param */
		JsonNode valueNode = postJson.get(tag);
		if (valueNode == null) {
			return null;
		}
		if(valueNode instanceof ArrayNode){
			Iterator<JsonNode> iter = ((ArrayNode)valueNode).iterator();
			LinkedList<String> result = new LinkedList<String>();
			while(iter.hasNext())
			{
				JsonNode curnode = iter.next();
				result.add(curnode.asText());
			}
			return (String[]) result.toArray();
		}
		return null;
	}

	

	private static Base64 base64Decoder = new Base64();

	public static String GetPagingKey(String string, ObjectNode reqJson) {

		JsonNode json = reqJson.get(string);
		if (json == null || !json.isTextual())
			return null;
		String key = json.asText();
		try {
			byte[] bkey = base64Decoder.decode(key);
			return new String(bkey);
		} catch (Exception e) {
			return null;
		}
	}

	public static boolean SetKeyandValue(ObjectNode reqJson, String key,
			String value) {
		try {
			byte[] encodedbytes = base64Decoder.encodeBase64(value.getBytes());
//			String encoded = base64Decoder.encodeBase64(value.getBytes());
			String encoded = new String(encodedbytes);
			if (encoded.endsWith("\r\n"))
				encoded = encoded.substring(0, encoded.length() - 2);
			reqJson.put(key, encoded);
			return true;
		} catch (Exception e) {
			logger.error(e.getClass().toString() + ":" + e.getMessage());
			return false;
		}
	}
	
	public static boolean SetKeyandValue(ObjectNode reqJson, String key,
			JsonNode value) {
		try {
			reqJson = (ObjectNode) reqJson.set(key, value);
			return true;
		} catch (Exception e) {
			logger.error(e.getClass().toString() + ":" + e.getMessage());
			return false;
		}
	}
	
	enum KeyMode {
		UNDEFINED, NO_KEY, ONLY_TS_KEY, TYPE_TS_KEY,
	}

	public static Map JsonNode2Map(JsonNode jsnode) {
		Map result = mapper.convertValue(jsnode, Map.class);
		return result;
	};
}
